package sg.edu.nus.iss.billing.controller;

import static sg.edu.nus.iss.billing.localization.MessageKeyManager.getMessage;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import sg.edu.nus.iss.billing.constant.ErrorCodeConstants;
import sg.edu.nus.iss.billing.datastore.DBFactory;
import sg.edu.nus.iss.billing.datastore.IDataStore;
import sg.edu.nus.iss.billing.datastore.Transaction;
import sg.edu.nus.iss.billing.datastore.impl.Selector;
import sg.edu.nus.iss.billing.entity.RecordState;
import sg.edu.nus.iss.billing.entity.Subscription;
import sg.edu.nus.iss.billing.entity.service.CableTVService;
import sg.edu.nus.iss.billing.entity.service.CableTVSubscription;
import sg.edu.nus.iss.billing.entity.service.DigitalVoiceService;
import sg.edu.nus.iss.billing.entity.service.DigitalVoiceSubscription;
import sg.edu.nus.iss.billing.entity.service.MobileVoiceService;
import sg.edu.nus.iss.billing.entity.service.MobileVoiceSubscription;
import sg.edu.nus.iss.billing.entity.service.PackageService;
import sg.edu.nus.iss.billing.entity.service.Service;
import sg.edu.nus.iss.billing.entity.service.SubscriptionEntity;
import sg.edu.nus.iss.billing.entity.service.VASSubscription;
import sg.edu.nus.iss.billing.entity.service.ValueAddedService;
import sg.edu.nus.iss.billing.exception.ControllerException;
import sg.edu.nus.iss.billing.exception.DataStoreException;
import sg.edu.nus.iss.billing.util.Util;

/**
 * 
 * @author RAJU NAVANEETHAM VENKATESHWARI
 *
 */
public class SubscriptionController {
	
private IDataStore ds;
	
	private ServiceController svcCtl;

	public SubscriptionController() {

		ds = DBFactory.instance().getTSupportDataStore();
		svcCtl = new ServiceController();
	}

	public List<SubscriptionEntity> getSubscribedPakSvcByAccNo(String accNo) throws ControllerException {

		List<SubscriptionEntity> list = null;
		Selector sel = new Selector();
		sel.eq("accNo", accNo).eq("subscriptionId", "").ne("state", RecordState.DELETED).ne("state", RecordState.INACTIVE);

		try {
			list = ds.loadObjectsBySelector(SubscriptionEntity.class, sel);
		} catch (DataStoreException e) {
			throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
		}
		return list;
	}

	public List<SubscriptionEntity> getVASSubscriptions(String subscriptionId) throws ControllerException {

		List<SubscriptionEntity> list = null;
		Selector sel = new Selector();
		sel.eq("subscriptionId", subscriptionId).eq("isBasicSvc",false).ne("state", RecordState.DELETED).ne("state", RecordState.INACTIVE);

		try {
			list = ds.loadObjectsBySelector(SubscriptionEntity.class, sel);
		} catch (DataStoreException e) {
			e.printStackTrace();
			throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
		}
		return list;
	}
	
	public List<SubscriptionEntity> getBasicSubscriptions(String subscriptionId) throws ControllerException {

		List<SubscriptionEntity> list = null;
		Selector sel = new Selector();
		sel.eq("subscriptionId", subscriptionId).eq("isBasicSvc",true).ne("state", RecordState.DELETED).ne("state", RecordState.INACTIVE);

		try {
			list = ds.loadObjectsBySelector(SubscriptionEntity.class, sel);
		} catch (DataStoreException e) {
			throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
		}
		return list;
	}
	
	public List<SubscriptionEntity> getAllSubscriptions(String subscriptionId) throws ControllerException {

		List<SubscriptionEntity> list = null;
		Selector sel = new Selector();
		sel.eq("subscriptionId", subscriptionId).ne("state", RecordState.DELETED).ne("state", RecordState.INACTIVE);

		try {
			list = ds.loadObjectsBySelector(SubscriptionEntity.class, sel);
		} catch (DataStoreException e) {
			throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
		}
		return list;
	}

	public Service getServiceById(String id) throws ControllerException {

		Service service = null;
		try {
			service = ds.loadObjectByID(Service.class, id);
		} catch (DataStoreException e) {
			throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
		}
		return service;
	}

	public List<? extends Service> getAllPkgSvcs(PackageService svc) throws ControllerException {
		try {
			if(svc instanceof CableTVService)
				return svcCtl.getAllCableTVServices();
			else if(svc instanceof MobileVoiceService)
				return svcCtl.getAllMobileVoiceServices();
			else if (svc instanceof DigitalVoiceService)
				return svcCtl.getAllDigitalVoiceServices();
			else
				return null;//throw exception latter
			
		} catch (DataStoreException e) {
			throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
		}
	}

	
	public void saveSubscription(SubscriptionEntity pkgSvc, List<SubscriptionEntity> svcs) throws ControllerException {

		if (pkgSvc ==null) {
			// throw new ControllerException(ErrorCodeConstants., message);
			return;
		}
		dateOverlapCheck(pkgSvc);
		if(duplicateSubscribeFound(svcs)) {
			throw new ControllerException(ErrorCodeConstants.ERROR_DUPICATE_SUBSCRIPTION, getMessage("error.subscription.duplicateSubscription"));
		}
		for (SubscriptionEntity sub: svcs) {
			dateOverlapCheck(sub);
			dateOverlapCheck(pkgSvc.getSubscribeDt(), sub.getSubscribeDt());
			sub.setSubscriptionId(pkgSvc.getId());
		}

		Transaction tran = null;
		try {
			tran = ds.getTransaction();
			tran.startTransaction();
			ds.saveObject(pkgSvc);
			if (! Util.isEmpty(svcs)) {
				for (SubscriptionEntity sub: svcs) {
					sub.setSubscriptionId(pkgSvc.getId());
				}
				ds.saveObjects(svcs);
			}

			tran.commit();

		} catch (DataStoreException e) {
			if (tran !=null)
				try {
					tran.rollback();
				} catch (DataStoreException e1) {
					throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
				}
				e.printStackTrace();
			throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
		} catch (Exception e) {
			try {
				tran.rollback();
			} catch (DataStoreException e1) {
				throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
			}
				throw new ControllerException(ErrorCodeConstants.UNKNOWN_ERROR, getMessage("error.unknown"));
		}
	}

	public void desubscribe(SubscriptionEntity sub) throws ControllerException {

		try {
			sub.setTerminateDt(new Date());
			sub.setState(RecordState.DELETED);
			List<SubscriptionEntity> vasSubscriptions = getAllSubscriptions(sub.getId());
			if(!Util.isEmpty(vasSubscriptions)){
				for(SubscriptionEntity vas:vasSubscriptions){
					vas.setTerminateDt(new Date());
					vas.setState(RecordState.DELETED);
				}
				vasSubscriptions.add(sub);
				ds.saveObjects(vasSubscriptions);
			} else {
				ds.saveObject(sub);
			}
		} catch (DataStoreException e) {
			e.printStackTrace();
			throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
		}
	}
	
	/**
	 * Get all the accNo's subscription
	 * @param accNo
	 * @return
	 * @throws ControllerException 
	 */
	public List<Subscription> getSubscriptionByAccNo(String accNo) throws ControllerException {
		List<Subscription> rtnList = new ArrayList<Subscription>();
		try {
			List<SubscriptionEntity> list = this.getSubscribedPakSvcByAccNo(accNo);
			if(!Util.isEmpty(list)){
				for(SubscriptionEntity sub:list){
					PackageService pks = (PackageService) getServiceById(sub.getSvcId());
					List<SubscriptionEntity> vasList = this.getAllSubscriptions(sub.getId());
					if(pks instanceof MobileVoiceService) {
						MobileVoiceSubscription mvs = new MobileVoiceSubscription();
						mvs.setId(sub.getId());
						mvs.setServicePhoneNumber(sub.getSvcNo());
						mvs.setSubscriptionName(pks.getServiceName());
						mvs.setSubscriptionDate(sub.getSubscribeDt());
						mvs.setTerminationDate(sub.getTerminateDt());
						mvs.setActive(sub.getState());
						mvs.setService((MobileVoiceService)pks);
						rtnList.add(mvs);
						if(!Util.isEmpty(vasList)){
							for(SubscriptionEntity vas:vasList){
								ValueAddedService valueAddedService = (ValueAddedService) this.getServiceById(vas.getSvcId());
								
								VASSubscription vASSubscription = new VASSubscription(mvs);
								vASSubscription.setId(vas.getId());
								vASSubscription.setBasicSvc(vas.getIsBasicSvc());
								vASSubscription.setService(valueAddedService);
								//vASSubscription.setServicePhoneNumber(vas.getSvcNo());
								vASSubscription.setSubscriptionName(valueAddedService.getServiceName());
								vASSubscription.setSubscriptionDate(vas.getSubscribeDt());
								vASSubscription.setTerminationDate(vas.getTerminateDt());
								vASSubscription.setActive(vas.getState());
								rtnList.add(vASSubscription);
							}
						}
						
					} else if(pks instanceof DigitalVoiceService) {
						DigitalVoiceSubscription dvs = new DigitalVoiceSubscription();
						dvs.setId(sub.getId());
						dvs.setServicePhoneNumber(sub.getSvcNo());
						dvs.setSubscriptionName(pks.getServiceName());
						dvs.setSubscriptionDate(sub.getSubscribeDt());
						dvs.setTerminationDate(sub.getTerminateDt());
						dvs.setActive(sub.getState());
						dvs.setService((DigitalVoiceService)pks);
						rtnList.add(dvs);
						if(!Util.isEmpty(vasList)){
							for(SubscriptionEntity vas:vasList){
								ValueAddedService valueAddedService = (ValueAddedService) this.getServiceById(vas.getSvcId());
								VASSubscription vASSubscription = new VASSubscription(dvs);
								vASSubscription.setId(vas.getId());
								vASSubscription.setBasicSvc(vas.getIsBasicSvc());
								vASSubscription.setService(valueAddedService);
								//vASSubscription.setServicePhoneNumber(vas.getSvcNo());
								vASSubscription.setSubscriptionName(valueAddedService.getServiceName());
								vASSubscription.setSubscriptionDate(vas.getSubscribeDt());
								vASSubscription.setTerminationDate(vas.getTerminateDt());
								vASSubscription.setActive(vas.getState());
								rtnList.add(vASSubscription);
							}
						}
						
					}  else if(pks instanceof CableTVService) {
						CableTVSubscription tvs = new CableTVSubscription();
						tvs.setId(sub.getId());
						tvs.setSubscriptionName(pks.getServiceName());
						tvs.setSubscriptionDate(sub.getSubscribeDt());
						tvs.setTerminationDate(sub.getTerminateDt());
						tvs.setActive(sub.getState());
						tvs.setService((CableTVService)pks);
						rtnList.add(tvs);
						if(!Util.isEmpty(vasList)){
							for(SubscriptionEntity vas:vasList){
								ValueAddedService valueAddedService = (ValueAddedService) this.getServiceById(vas.getSvcId());
								VASSubscription vASSubscription = new VASSubscription(tvs);
								vASSubscription.setId(vas.getId());
								vASSubscription.setBasicSvc(vas.getIsBasicSvc());
								vASSubscription.setService(valueAddedService);
								vASSubscription.setSubscriptionName(valueAddedService.getServiceName());
								vASSubscription.setSubscriptionDate(vas.getSubscribeDt());
								vASSubscription.setTerminationDate(vas.getTerminateDt());
								vASSubscription.setActive(vas.getState());
								rtnList.add(vASSubscription);
							}
						}
						
					} else {
						//do nothing here
					}
				}
			}
		} catch (ControllerException e) {
			e.printStackTrace();
			throw new ControllerException(ErrorCodeConstants.ERROR_DATASTORE, getMessage("error.dataStore"));
		}
		return rtnList;
	}
	
	private void dateOverlapCheck(SubscriptionEntity sub) throws ControllerException{
		if(sub != null) {
			if(sub.getTerminateDt() != null) {
				if(sub.getTerminateDt().before(sub.getSubscribeDt())) {
					throw new ControllerException(ErrorCodeConstants.ERROR_TEMINATE_DT_OVERLAP, getMessage("error.date.overlap"));
				}
			}
		}
	}
	
	private void dateOverlapCheck(Date planStartDt, Date svcStartDt) throws ControllerException{
		if(planStartDt != null && svcStartDt != null) {
			if(svcStartDt.before(planStartDt)) {
				throw new ControllerException(ErrorCodeConstants.ERROR_OPTIONA_BASIC_TEMINATE_DT_OVERLAP, getMessage("error.date.basic.optional.overlap"));
			}
		}
	}
	
	private boolean duplicateSubscribeFound(List<SubscriptionEntity> subscription){
		boolean result = false;
		if(subscription != null) {
			Set<String> set = new HashSet<String>();
			for(SubscriptionEntity sub :subscription){
				set.add(sub.getSvcId());
			}
			
			if(set.size() != subscription.size()){
				result = true;
			}
		}
		
		return result;
	}
}
